Redis从出门到高可用--sentinel哨兵模式

Redis从出门到高可用–sentinel哨兵模式

一、主从复制到高可用存在的问题

1、手动故障转移
    例如当master宕机,我们在某台slave上执行slaveof no one命令让这个从节点不再属于任何master,自己成为master,其
    余slave执行slaveof new master命令来对这个新master进行同步,但这里会有个问题,我
    们不知道何时master节点会不可用,无法及时手动去解决,而且出现这种问题我们容易手忙脚乱。还有当master发生改变时,如
    何通知我们的客户端?
2、写能力和存储能力受到限制

二、架构说明

image

首先,你可以把一个sentinel想象是一个redis的进程,不同的是sentinel不负责存储数据,它是负责对redis的一个故障判断、故障转
移以及通知客户端的功能。另外,由上图可以看出sentinel不是一个而是多个,这样一来可以保证我们判断故障的一个公平性(后面
可以设置几个sentinel认为节点有故障才算数),同时也保证了我们的高可用(即当一个sentinel节点挂了,仍然可以保证我们这个sentinel机制是完美的)。

那对客户端来说就再也不会直接从redis中获取信息,也就是说在我们客户端中不会记录redis的地址(某个IP),而是记录sentinel的
地址,这样我们可以直接从sentinel获取的redis地址,因为sentinel会对所有的master、slave进行监控,它是知道到底谁才是真正的
master的,例如我们故障转移,这时候对于sentinel来说,master是变了的,然后通知客户端。而客户端根本不用关心到底谁才是真正
的master,只关心sentinel告知的master。



redis sentinel故障转移的步骤:
(1)、多个sentinel发现并确认master有问题;
(2)、选举出一个sentinel作为领导。(因为故障转移一系列操作只需要一个sentinel就可以完成);
(3)、从多个slave中选出一个slave作为新的master;
(4)、通知其余slave成为新的master的slave;
(5)、通知客户端主从变化(这样客户端就不会有读取失败的问题);
(6)、等待老的master复活成为新的master的slave(sentinel依然会对老的master进行监控是否复活);

这里简单提一下:我们的一套sentinel是可以监听多套master+slave的组合,这样可以有效节省资源,其中每套master+slave会使用一个master-name作为一个标识。

三、安装配置

1、配置开启主从节点
2、配置开启sentinel监控主节点。(sentinel是特殊的redis)
3、实际应该多机器
4、详细配置节点;

最终配置的效果如下:

image

对主节点的配置:

启动:redis-server redis-7000.conf

配置:port 7000
      daemonize yes
      pidfile /var/run/redis-7000.pid
      logfile "7000.log"
      dir "/root/redis/data/"

对从节点的配置:

slave-1
        port 7001
        daemonize yes
        pidfile /var/run/redis-7001.pid
        logfile "7001.log"
        dir "/root/redis/data/"
        slaveof 127.0.0.1 7000

slave-2      
        port 7002
        daemonize yes
        pidfile /var/run/redis-7002.pid
        logfile "7002.log"
        dir "/root/redis/data/"
        slaveof 127.0.0.1 7000

启动:redis-server redis-7001.conf
      redis-server redis-7002.conf


sentinel主要配置:

port ${port}
dir "/root/redis/data/"
logfile "${port}.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-synec mymaster 1
sentinel failover-timeout mymaster 180000

实验

redis-7000.conf:
port 7000
daemonize yes
pidfile /var/run/redis-7000.pid
logfile "7000.log"
dir "/root/redis/data/"

redis-7001.conf:
port 7001
daemonize yes
pidfile /var/run/redis-7001.pid
logfile "7001.log"
dir "/root/redis/data/"
slaveof 127.0.0.1 7000


redis-7002.conf:
port 7002
daemonize yes
pidfile /var/run/redis-7002.pid
logfile "7002.log"
dir "/root/redis/data/"
slaveof 127.0.0.1 7000

分别启动三个redis实例:
[root@promote config]# redis-server redis-7000.conf 
[root@promote config]# redis-server redis-7001.conf 
[root@promote config]# redis-server redis-7002.conf 

查看master信息:

[root@promote config]# redis-cli -p 7000 info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7001,state=online,offset=29,lag=1
slave1:ip=127.0.0.1,port=7002,state=online,offset=29,lag=1
master_repl_offset:29
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:28

查看slave信息:

[root@promote config]# redis-cli -p 7001 info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:7000
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:435
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

[root@promote config]# redis-cli -p 7002 info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:7000
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:463
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0


对redis sentinel配置:
reids-sentinel-26379.conf:
port 26379
daemonize yes
dir "/root/redis/data"
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000


redis-sentinel-26380.conf:

port 26380
daemonize yes
dir "/root/redis/data"
logfile "26380.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000


redis-sentinel-26381.conf:

port 26381
daemonize yes
dir "/root/redis/data"
logfile "26381.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

分别启动三个实例:

redis-sentinel redis-sentinel-26379.conf 
redis-sentinel redis-sentinel-26380.conf 
redis-sentinel redis-sentinel-26381.conf 

查看配置文件redis-sentinel-26379.conf的变化:
port 26379
daemonize yes
dir "/root/redis-3.0.3/data"
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel known-slave mymaster 127.0.0.1 7001
# Generated by CONFIG REWRITE
sentinel known-slave mymaster 127.0.0.1 7002
sentinel known-sentinel mymaster 127.0.0.1 26381 c698ffdc6c0ae865369161ad265288beebb3c2dd
sentinel known-sentinel mymaster 127.0.0.1 26379 91d09f9acde747c9c5f0105497d8f3353364b25e
sentinel current-epoch 0

26380与26381类似。

启动redis-cli客户端:
[root@promote config]# redis-cli -p 26379 
[root@promote config]# redis-cli -p 26380 
[root@promote config]# redis-cli -p 26381 

查看信息

127.0.0.1:26380> info


# Server
redis_version:3.0.3
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:b042e46a9c1e1b6c
redis_mode:sentinel
os:Linux 3.10.0-514.10.2.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:3383
run_id:6aa2c586c70b76a91cb22763ca5d31c99ada406a
tcp_port:26380
uptime_in_seconds:159
uptime_in_days:0
hz:13
lru_clock:1802516
config_file:/root/redis-3.0.3/config/redis-sentinel-26380.conf

# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
127.0.0.1:26380> exit
[root@promote config]# redis-cli -p 26381 
127.0.0.1:26381> info
# Server
redis_version:3.0.3
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:b042e46a9c1e1b6c
redis_mode:sentinel
os:Linux 3.10.0-514.10.2.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:3452
run_id:c698ffdc6c0ae865369161ad265288beebb3c2dd
tcp_port:26381
uptime_in_seconds:43
uptime_in_days:0
hz:17
lru_clock:1802530
config_file:/root/redis-3.0.3/config/redis-sentinel-26381.conf

# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3

其他类似


四、客户端连接

4.1、请求响应流程

(1).首先要获取所有sentinel节点的集合,获取一个可用节点,同时需要一个对应的masterName;

image

(2).通过sentinel的一个api:get-master-addr-by-name-masterName(通过名字获取地址),sentinel会返回master的真正地址和端口;

image

(3).当客户端获取到master信息后,会通过role replication来进行一个验证是否是真正的master节点

image

(4).最后一步就是当sentinel感知到master的变化会通知客户端更换节点,其实内部是用的一个发布订阅模式(客户端订阅sentinel
的某一个频道,当master发生变化,sentinel向这个频道publish一条消息,客户端就可以获取再对新的master进行一个连接)

image

4.2、jedis
(1).需要一个sentinel地址的集合

(2).需要masterName

(3).不是代理模式(不是每次都需要去连接sentinel节点去获取master信息,这样效率很差,而是采用通知的形式)

使用Jedis访问sentinel:
//初始化Sentinel连接池,注意:这里名字是JedisSentinelPool只是为了区分它是sentinel方式连接,其内部还是连接master
JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName,SentinelSet,poolConfig,timeout);

Jedis jedis = null;
try{
  jedis = sentinelPool.getResource();
  //这里执行jedis command
}catch(Exception e){
  logger.error(e.getMessage(),e);
}finally{
  if(jedis != null)
     //归还连接
     jedis.close();
}

五、实现原理

5.1、 三个定时任务
sentinel在内部有3个定时任务
(1)、每10秒每个sentinel会对master和slave执行info命令;
这个任务达到两个目的:

发现slave节点

确认主从关系

image

(2)、每2秒每个sentinel通过master节点的channel交换信息(pub/sub);

master节点上有一个发布订阅的频道(__sentinel__:hello)。

sentinel节点通过__sentinel__:hello频道进行信息交换(对节点的"看法"和自身的信息),达成共识。

image

(3)、每1秒每个sentinel对其他sentinel和redis节点执行ping操作(相互监控)

这个其实是一个心跳检测,是失败判定的依据。

image

5.2、主观下线和客观下线

在redis-sentinel的conf文件里有这么两个配置:

(1)、sentinel monitor <masterName> <ip> <port> <quorum>
四个参数含义:

masterName这个是对某个master+slave组合的一个区分标识(一套sentinel是可以监听多套master+slave这样的组合的)。

ip 和 port 就是master节点的 ip 和 端口号。

quorum这个参数是进行客观下线的一个依据,意思是至少有 quorum 个sentinel主观的认为这个master有故障,才会对这个ma
ster进行下线以及故障转移。因为有的时候,某个sentinel节点可能因为自身网络原因,导致无法连接master,而此时master并没有
出现故障,所以这就需要多个sentinel都一致认为该master有问题,才可以进行下一步操作,这就保证了公平性和高可用。

(2)、sentinel down-after-milliseconds <masterName> <timeout> 
这个配置其实就是进行主观下线的一个依据,masterName这个参数不用说了,timeout是一个毫秒值,表示:如果这台sentinel超过t
imeout这个时间都无法连通master包括slave(slave不需要客观下线,因为不需要故障转移)的话,就会主观认为该master已经下线
(实际下线需要客观下线的判断通过才会下线);

那么,多个sentinel之间是如何达到共识的呢?
这就是依赖于前面说的第二个定时任务,某个sentinel先将master节点进行一个主观下线,然后会将这个判定通过sentinel  is-master-down-by-addr这个命令
问对应的节点是否也同样认为该addr的master节点要做客观下线。最后当达成这一共识的sentinel
个数达到前面说的quorum设置的这个值时,就会对该master节点下线进行故障转移。quorum的值一般设置为sentinel个数的二分之一
加1,例如3个sentinel就设置2



5.3、领导者选举
为什么要选领导者?因为只能有一个sentinel节点去完成故障转移;
sentinel is-master-down-by-addr这个命令有两个作用,一是确认下线判定,二是进行领导者选举。

选举过程:
(1).每个做主观下线的sentinel节点向其他sentinel节点发送上面那条命令,要求将它设置为领导者。

(2).收到命令的sentinel节点如果还没有同意过其他的sentinel发送的命令(还未投过票),那么就会同意,否则拒绝。

(3).如果该sentinel节点发现自己的票数已经过半且达到了quorum的值,就会成为领导者

(4).如果这个过程出现多个sentinel成为领导者,则会等待一段时间重新选举。



5.4、故障转移
所谓故障转移就是当master宕机,选一个合适的slave来晋升为master的操作,redis-sentinel会自动完成这个,不需要我们手动来实现。

那么,如何选择一个合适的slave呢?顺序如下:

(1).选择slave-priority(slave节点优先级配置)最高的slave节点,(默认都是一样的)例如:如果我们有两台slave在两台机器
上,一台配置较高,我们希望当master挂掉优先选配置高的,就可以配置该值为slave中最高的。如果存在最高则返回,不存在继续;

(2).选择复制偏移量最大的节点(复制得最完整,与master节点的数据一致性更高),如果存在则返回,不存在继续;

(3).如果以上两个条件都不满足,选runId最小的(启动最早的);

补充一点:还可以向任意sentinel发生sentinel failover <masterName> 进行手动故障转移,这样就不需要经过上述主客观和选举的过程。

六、常见的开发运维问题

1、节点运维



2、高可用读写分离
文章目录
  1. 1. 一、主从复制到高可用存在的问题
  2. 2. 二、架构说明
  3. 3. 三、安装配置
  4. 4. 四、客户端连接
  5. 5. 五、实现原理
  6. 6. 六、常见的开发运维问题